package rpc

import (
	"strconv"
	"time"

	"hundsun.com/hsl/hschain/common/util"

	"github.com/golang/protobuf/ptypes/timestamp"
	protoscommon "hundsun.com/hsl/hschain/protos/common"
	"hundsun.com/hsl/hschain/protos/ledger"
	"hundsun.com/hsl/hschain/rpc/common/constant"
	"hundsun.com/hsl/hschain/rpc/common/rpcerror"
)

// IBlockSubscribe block subscribe
type IBlockSubscribe interface {
	/**
	 * @Result 获取请求起始区块
	 */
	GetStartBlockNum() string
	/**
	 * @Result 获取请求结束区块
	 */
	GetEndBlockNum() string
	/**
	 * @Result 获取订阅subid
	 */
	GetSubId() string
	/**
	 * @Param 已执行区块
	 * @Result 1.区块是否满足查询条件，2.订阅是否可以结束, 3.过滤结果
	 */
	Filter(block *ledger.ExecutedBlock, subMark Result_SubMark) (bool, bool, []*Result)

	GetTimestamp() *timestamp.Timestamp

	IsExpire() bool
	/**
	 * @Param 已执行区块
	 * @Result 封装subscribe result
	 */
	//GetResult(*ledger.ExecutedBlock) *Result
}

// GetStartBlockNum ...
func (sub *TransactionSubInfo) GetStartBlockNum() string {
	return "0"
}

// GetEndBlockNum ...
func (sub *TransactionSubInfo) GetEndBlockNum() string {
	return "0"
}

// IsExpire ...
func (sub *TransactionSubInfo) IsExpire() bool {
	if sub == nil || sub.Timestamp == nil {
		return true
	}
	nowSecond := time.Now().Unix()
	return nowSecond-sub.Timestamp.Seconds > constant.TxSubExpireTime
}

// Filter 查找txhash
func (sub *TransactionSubInfo) Filter(block *ledger.ExecutedBlock, subMark Result_SubMark) (bool, bool, []*Result) {
	txHash := sub.TxHash
	//判空
	if block == nil || block.BlockExecResult == nil || block.BlockExecResult.TransactionExecResults == nil {
		return false, false, []*Result{}
	}

	exeResults := block.BlockExecResult.TransactionExecResults
	for _, result := range exeResults {

		//判空
		if result.Receipt == nil || result.Receipt.TxHash == nil {
			continue
		}
		if string(txHash) == string(result.Receipt.TxHash) && result.Receipt.TxStatus != ledger.TransactionReceipt_DUPLICATED_TX {
			buffer, err := util.Struct2Bytes(result.Receipt)
			if err != nil {
				return false, false, []*Result{{
					SubType:  Result_TXRESULT,
					SubId:    sub.SubId,
					BlockNum: block.BlockExecResult.BlockNumber,
					Status:   rpcerror.SerializeFail.GetStatus(),
					Message:  rpcerror.SerializeFail.GetMessage(),
					SubMark:  subMark,
				}}
			}
			return true, true, []*Result{{
				SubType:  Result_TXRESULT,
				SubId:    sub.SubId,
				BlockNum: block.BlockExecResult.BlockNumber,
				Status:   rpcerror.Success.GetStatus(),
				Message:  rpcerror.Success.GetMessage(),
				Payload:  buffer,
				SubMark:  subMark,
			}}
		}
	}
	return false, false, nil
}

// IsExpire is expire
func (sub *BlockSubInfo) IsExpire() bool {
	return false
}

// Filter block filter
func (sub *BlockSubInfo) Filter(block *ledger.ExecutedBlock, subMark Result_SubMark) (bool, bool, []*Result) {

	//判空
	if block == nil || block.BlockExecResult == nil {
		return false, false, []*Result{}
	}

	buffer, err := util.Struct2Bytes(block)
	if err != nil {
		return true, false, []*Result{{
			SubType: Result_BLOCK,
			SubId:   sub.SubId,
			Status:  rpcerror.SerializeFail.GetStatus(),
			Message: rpcerror.SerializeFail.GetMessage(),
			SubMark: subMark,
		}}
	}
	return true, false, []*Result{{
		SubType:  Result_BLOCK,
		SubId:    sub.SubId,
		BlockNum: block.BlockExecResult.BlockNumber,
		Status:   rpcerror.Success.GetStatus(),
		Message:  rpcerror.Success.GetMessage(),
		Payload:  buffer,
		SubMark:  subMark,
	}}
}

// IsExpire is expire
func (sub *ContractEventSubInfo) IsExpire() bool {
	return false
}

// Filter 验证块中是否包含某个合约
func (sub *ContractEventSubInfo) Filter(block *ledger.ExecutedBlock, subMark Result_SubMark) (bool, bool, []*Result) {

	var results []*Result

	//判空
	if block == nil || block.BlockExecResult == nil || block.Block == nil || block.Block.Header == nil || block.BlockExecResult.TransactionExecResults == nil {
		return false, false, results
	}

	isEnd := sub.end(block.Block.Header.Number)

	transactions := block.BlockExecResult.TransactionExecResults
	//遍历transaction
	for _, transaction := range transactions {

		//判空
		if transaction.Receipt == nil || transaction.Receipt.Events == nil {
			continue
		}

		//遍历account
		for _, event := range transaction.Receipt.Events {
			equal := sub.Compare(event)
			if equal {
				buffer, err := util.Struct2Bytes(event)
				if err != nil {
					results = append(results, &Result{
						SubType:  Result_CONTRACTEVENT,
						SubId:    sub.SubId,
						BlockNum: block.BlockExecResult.BlockNumber,
						Status:   rpcerror.SerializeFail.GetStatus(),
						Message:  rpcerror.SerializeFail.GetMessage(),
						Payload:  buffer,
						SubMark:  subMark,
					})
				} else {
					results = append(results, &Result{
						SubType:  Result_CONTRACTEVENT,
						SubId:    sub.SubId,
						BlockNum: block.BlockExecResult.BlockNumber,
						Status:   rpcerror.Success.GetStatus(),
						Message:  rpcerror.Success.GetMessage(),
						Payload:  buffer,
						SubMark:  subMark,
					})
				}
			}
		}
	}
	if len(results) == 0 {
		results = append(results, &Result{
			SubType:  Result_CONTRACTEVENT,
			SubId:    sub.SubId,
			SubMark:  subMark,
			BlockNum: block.BlockExecResult.BlockNumber,
			Status:   rpcerror.Success.GetStatus(),
			Message:  rpcerror.Success.GetMessage(),
		})
	}
	return true, isEnd, results
}

func (sub *ContractEventSubInfo) end(blkNum uint64) bool {
	endNum, err := strconv.ParseUint(sub.EndBlockNum, 10, 64)
	if err != nil || blkNum > endNum {
		return true
	}
	return false
}

func (sub *ContractEventSubInfo) rangeIn(blkNum uint64) bool {
	startNum, err1 := strconv.ParseUint(sub.StartBlockNum, 10, 64)
	endNum, err2 := strconv.ParseUint(sub.EndBlockNum, 10, 64)
	if err1 != nil || err2 != nil {
		return false
	}
	if blkNum < startNum || blkNum > endNum {
		return false
	}
	return true
}

// Compare 订阅者与该event比较
func (sub *ContractEventSubInfo) Compare(event *protoscommon.ContractEvent) bool {
	if event == nil {
		return false
	}
	if event.GetContractId() == nil {
		return false
	}
	if sub.GetName() != event.GetName() {
		return false
	}
	in := sub.rangeIn(event.BlockNumber)
	if !in {
		return in
	}

	if sub.GetContractId() == nil || sub.GetContractId().Name != event.GetContractId().Name {
		return false
	}

	return sub.compareIndex(string(sub.IndexedParamFirst), event.IndexedParamFirst) &&
		sub.compareIndex(string(sub.IndexedParamSecond), event.IndexedParamSecond) &&
		sub.compareIndex(string(sub.IndexedParamThird), event.IndexedParamThird)
}

func (sub *ContractEventSubInfo) compareIndex(paramIndex string, eventIndex string) bool {
	return paramIndex == "" || paramIndex == eventIndex
}
